import json
import argparse
import re
import os
from typing import Any, List, Optional

def load_json(path: str) -> Any:
	"""加载 JSON 文件"""
	with open(path, 'r', encoding='utf-8') as f:
		return json.load(f)

def _is_empty_value(v: Any) -> bool:
	"""判断一个值是否为空（空字符串、空列表、空字典、None）"""
	if v is None:
		return True
	if isinstance(v, str) and v.strip() == "":
		return True
	if isinstance(v, (list, tuple, set)) and len(v) == 0:
		return True
	if isinstance(v, dict) and len(v) == 0:
		return True
	return False

def find_empty_fields(obj: Any, prefix: str = "") -> List[str]:
	"""递归查找空字段，返回字段路径列表（例如：患者信息.现住址）"""
	empties = []
	if isinstance(obj, dict):
		for k, v in obj.items():
			path = f"{prefix}.{k}" if prefix else k
			if _is_empty_value(v):
				empties.append(path)
			else:
				empties.extend(find_empty_fields(v, path))
	elif isinstance(obj, list):
		for idx, item in enumerate(obj):
			path = f"{prefix}[{idx}]"
			if _is_empty_value(item):
				empties.append(path)
			else:
				empties.extend(find_empty_fields(item, path))
	# 基本类型且非空则不返回
	return empties

def check_specific_rules(data: dict) -> List[str]:
	"""检查年龄、联系电话等硬规则，返回错误消息列表（仅对非空值做格式校验，空值由空字段检测负责）"""
	errs = []

	# 年龄规则：数字+岁，例如"34岁"
	try:
		age_val = data.get("患者信息", {}).get("年龄", None)
	except AttributeError:
		age_val = None
	if age_val is not None and not (isinstance(age_val, str) and age_val.strip() == ""):
		# 仅当非空时才做格式校验
		if not re.match(r"^\d+岁$", age_val):
			errs.append(f"年龄字段格式不规范（患者信息.年龄）：{age_val}，应为数字+岁，例如'34岁'")

	# 联系电话规则：必须为11位数字
	phone_val = data.get("患者信息", {}).get("联系电话", None)
	if phone_val is not None and not (isinstance(phone_val, str) and phone_val.strip() == ""):
		# 仅当非空时才做格式校验
		if not re.match(r"^\d{11}$", phone_val):
			errs.append(f"联系电话格式不规范（患者信息.联系电话）：{phone_val}，应为11位数字，例如'13800138000'")

	# 检查若干关键字段是否存在且非空
	required_fields = [
		("医院名称",),
		("病历类型",),
		("患者信息", "姓名"),
		("病情信息", "主诉"),
		("病情信息", "诊断"),
	]
	for path in required_fields:
		curr = data
		for key in path:
			if not isinstance(curr, dict) or key not in curr:
				errs.append(f"必填字段缺失：{'.'.join(path)}")
				curr = None
				break
			curr = curr[key]
		else:
			# 完成循环且 curr 为最终值
			# 对于必填字段若为空，仍保留该条错误（与空字段报告可能重复，但必填性检查通常需要保留）
			if _is_empty_value(curr):
				errs.append(f"必填字段为空：{'.'.join(path)}")

	return errs

def build_basic_error_list(data: dict) -> List[str]:
	"""构建'基础错误列表'，包含空字段与硬规则错误"""
	errors = []
	# 空字段检查（列出所有空字段路径）
	empties = find_empty_fields(data)

	# 不再过滤特定字段，空字段统一以 "<路径> 字段为空" 的格式报告
	for p in empties:
		errors.append(f"{p} 字段为空")
	# 特定硬规则检查（仅格式校验）
	errors.extend(check_specific_rules(data))
	# 去重并保持顺序
	seen = set()
	result = []
	for e in errors:
		if e not in seen:
			seen.add(e)
			result.append(e)
	return result

# 新增：将整个加载+校验流程封装为可被调用的函数，返回字典格式结果
def run_standard_check(input_path: Optional[str] = None) -> dict:
	"""
	封装主流程：加载 JSON、执行基础完整性校验并返回结果字典。
	如果 input_path 为 None，则使用与原 main 相同的默认文件路径。
	"""
	if input_path is None:
		input_path = os.path.join(os.path.dirname(__file__), "data", "3.json")
	data = load_json(input_path)
	basic_errors = build_basic_error_list(data)
	output = {
		"基础错误列表": basic_errors
	}
	return output

# def main():
# 	parser = argparse.ArgumentParser(description="基础完整性校验节点（硬规则）")
# 	parser.add_argument("--input", "-i", type=str,
# 						default=os.path.join(os.path.dirname(__file__), "data", "3.json"),
# 						help="待校验的 JSON 文件路径，默认 ./src/data/3.json")
# 	args = parser.parse_args()
# 	# 使用封装的函数获取结果字典，便于模块化调用
# 	output = run_standard_check(args.input)
# 	print(output)

# if __name__ == "__main__":
# 	main()